﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

namespace SuperCaptcha.Mvc.Processor
{
    internal sealed class ImageProcessor
    {
        private string Text { get; set; }

        private Bitmap Image { get; set; }

        private int Width { get; set; }

        private int Height { get; set; }

        internal ImageProcessor() { }

        internal ImageProcessor(string captchaCode, int imageWidth = 0, int imageHeight = 0)
        {
            Text = captchaCode;
            SetDimensions(imageWidth, imageHeight);
            SetFamilyName(fontList[random.Next(0, fontList.Length - 1)]);
        }

        internal void Dispose()
        {
            GC.SuppressFinalize(this);
        }


        private string familyName = String.Empty;
        private readonly String[] fontList = new String[] 
        { 
            "Times New Roman", "Verdana", 
            "Comic Sans MS", "Arial", 
            "Courier New", "Georgia", 
            "Impact", "Palatino Linotype", 
            "Lucida Console", "Marlett" 
        };
        private Random random = new Random();


        private void SetDimensions(int width, int height)
        {
            Height = (height <= 0) ? 40 : height;
            Width = (width <= 0) ? 140 : width;
        }


        private void SetFamilyName(string fontFamily)
        {
            try
            {
                Font font = new Font(familyName, 12F);
                familyName = fontFamily;
                font.Dispose();
            }
            catch
            {
                familyName = FontFamily.GenericSerif.Name;
            }
        }

        internal string GenerateImage()
        {
            Bitmap bitmap = new Bitmap(Width, Height, PixelFormat.Format32bppArgb);
            Graphics graphics = Graphics.FromImage(bitmap);
            graphics.SmoothingMode = SmoothingMode.HighQuality;
            Rectangle rect = new Rectangle(0, 0, Width, Height);

            Color backColor = Color.FromArgb(random.Next(128, 255), random.Next(128, 255), random.Next(128, 255));
            Color foreColor = InvertColor(backColor);

            HatchBrush hatchBrush = new HatchBrush(RandomEnum<HatchStyle>(), backColor, Color.White);
            graphics.FillRectangle(hatchBrush, rect);

            SizeF size;
            float fontSize = rect.Height + 11;
            Font font;
            do
            {
                fontSize--;
                font = new Font(this.familyName, fontSize, FontStyle.Bold);
                size = graphics.MeasureString(Text, font);
            } while (size.Width > rect.Width);

            StringFormat format = new StringFormat();
            format.Alignment = StringAlignment.Center;
            format.LineAlignment = StringAlignment.Center;

            GraphicsPath path = new GraphicsPath(FillMode.Alternate);
            path.AddString(this.Text, font.FontFamily, (Int32)font.Style, font.Size, rect, format);
            float vertex = 4F;
            PointF[] points =
            {
                new PointF(this.random.Next(rect.Width)/vertex, random.Next(rect.Height)/vertex),
                new PointF(rect.Width - this.random.Next(rect.Width)/vertex, random.Next(rect.Height)/vertex),
                new PointF(this.random.Next(rect.Width)/vertex, rect.Height - random.Next(rect.Height)/vertex),
                new PointF(rect.Width - this.random.Next(rect.Width)/vertex, rect.Height - random.Next(rect.Height)/vertex)
            };

            Matrix matrix = new Matrix();
            matrix.Translate(0F, 0F);
            path.Warp(points, rect, matrix, WarpMode.Perspective, 0F);
            
            hatchBrush = new HatchBrush(RandomEnum<HatchStyle>(), Color.DimGray, foreColor);
            graphics.FillPath(hatchBrush, path);

            Int32 maxDimension = Math.Max(rect.Width, rect.Height);
            for (Int32 i = 0; i < (Int32)(rect.Width * rect.Height / 30F); i++)
            {
                Int32 x = random.Next(rect.Width);
                Int32 y = random.Next(rect.Height);
                Int32 w = random.Next(maxDimension / 50);
                Int32 h = random.Next(maxDimension / 50);
                graphics.FillEllipse(hatchBrush, x, y, w, h);
            }

            font.Dispose();
            hatchBrush.Dispose();
            graphics.Dispose();

            return bitmap.GetBase64String();
        }

        private T RandomEnum<T>()
        {
            T[] values = (T[])Enum.GetValues(typeof(T));
            return values[new Random().Next(0, values.Length)];
        }

        private Color InvertColor(Color invertColor)
        {
            return Color.FromArgb(invertColor.A, Color.White.R - invertColor.R, Color.White.G - invertColor.G, Color.White.B - invertColor.B);
        }
    }    
}
